package ru.spb.osll.myfit.client.widget;

import com.google.gwt.gen2.table.client.*;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.ui.*;
import ru.spb.osll.myfit.common.*;

import java.util.*;

/**
 * User: Artem Bukhonov
 * Email: nerzhulart@gmail.com
 */

public class ResizableResultTable implements ResultTable {

  private static int RESULTS_PER_PAGE = 20;

  private List<ExecutingResult> m_data = new ArrayList<ExecutingResult>();
  private PagingScrollTable<ExecutingResult> m_tableWidget;
  private FlexTable m_flexTable = new FlexTable();
  private CachedTableModel<ExecutingResult> m_cachedTableModel;
  private ResultTableModel m_resultTableModel;
  DefaultTableDefinition<ExecutingResult> m_tableDefinition;
  private List<ResultTableEventListener> m_eventListeners = new ArrayList<ResultTableEventListener>();

  public ResizableResultTable() {
    createTableModel();

    m_tableDefinition = createTableDefinition();

    createPagingScrollTable(m_tableDefinition);

    // setup the bulk renderer
    m_tableWidget.setBulkRenderer(new FixedWidthGridBulkRenderer<ExecutingResult>(m_tableWidget.getDataTable(), m_tableWidget));

    setupFormatting();
  }

  private void setupFormatting() {
    m_tableWidget.setCellSpacing(0);
    m_tableWidget.setResizePolicy(ScrollTable.ResizePolicy.FLOW);
    m_tableWidget.setSortPolicy(AbstractScrollTable.SortPolicy.SINGLE_CELL);
    m_tableWidget.setWidth("100%");
    m_tableWidget.setHeight("100%");
    m_tableWidget.fillWidth();
  }

  private void createPagingScrollTable(DefaultTableDefinition<ExecutingResult> m_tableDefinition) {
    m_tableWidget = new PagingScrollTable<ExecutingResult>(m_cachedTableModel, m_tableDefinition);
    m_tableWidget.setPageSize(RESULTS_PER_PAGE);
    PagingOptions pagingOptions = new PagingOptions(m_tableWidget);
    m_flexTable.setStyleName(IStyleConstants.TEST_RESULTS_FLEX_TABLE_STYLE);
    m_flexTable.setWidget(0, 0, m_tableWidget);
    m_flexTable.setWidget(1, 0, pagingOptions);
    m_flexTable.getFlexCellFormatter().setAlignment(1, 0, HasHorizontalAlignment.ALIGN_CENTER, HasVerticalAlignment.ALIGN_MIDDLE);
    m_flexTable.setWidth("100%");
    m_flexTable.setHeight("100%");
    pagingOptions.setHeight("30px");
    pagingOptions.setVisible(true);
    m_tableWidget.setEmptyTableWidget(new HTML("No data to display"));
    m_tableWidget.getDataTable().setSelectionPolicy(SelectionGrid.SelectionPolicy.ONE_ROW);
  }

  private DefaultTableDefinition<ExecutingResult> createTableDefinition() {
    DefaultTableDefinition<ExecutingResult> m_tableDefinition = new DefaultTableDefinition<ExecutingResult>();
    m_tableDefinition.setRowRenderer(new RowRenderer<ExecutingResult>() {
      @Override
      public void renderRowValue(ExecutingResult rowValue, TableDefinition.AbstractRowView<ExecutingResult> view) {
        String style = "";
        switch (rowValue.getState()) {
          case PROGRESS:
            style = IStyleConstants.TEST_TABLE_PROGRESS_ROW_STYLE;
            break;
          case FAIL:
            style = IStyleConstants.TEST_TABLE_FAIL_ROW_STYLE;
            break;
          case FINISH:
            if (rowValue.isWrong()) {
              style = IStyleConstants.TEST_TABLE_EXCEPT_OR_WRONG_ROW_STYLE;
            } else {
              style = IStyleConstants.TEST_TABLE_SUCCESS_ROW_STYLE;
            }
            break;
          case NOT_STARTED:
            style = IStyleConstants.TEST_TABLE_NOT_STARTED_ROW_STYLE;
            break;
        }
        view.setStyleName(style);
      }
    });
    // id
    {
      PageNameColumn columnDef = new PageNameColumn();
      columnDef.setColumnSortable(true);
      columnDef.setColumnTruncatable(true);
      columnDef.setPreferredColumnWidth(300);
      columnDef.setMinimumColumnWidth(100);
      columnDef.setHeader(0, new HTML("Name"));
      columnDef.setHeaderCount(1);
      columnDef.setHeaderTruncatable(false);
      m_tableDefinition.addColumnDefinition(columnDef);
    }

    //progress
    {
      ProgressColumn columnDef = new ProgressColumn();
      columnDef.setColumnSortable(true);
      columnDef.setColumnTruncatable(false);
      columnDef.setPreferredColumnWidth(50);
      columnDef.setMinimumColumnWidth(50);
      columnDef.setHeader(0, new HTML("Progress"));
      columnDef.setHeaderCount(1);
      columnDef.setHeaderTruncatable(false);
      m_tableDefinition.addColumnDefinition(columnDef);
    }
    //Summary
    {
      SummaryColumn columnDef = new SummaryColumn();
      columnDef.setColumnSortable(false);
      columnDef.setColumnTruncatable(false);
      columnDef.setPreferredColumnWidth(100);
      columnDef.setHeader(0, new HTML("Summary"));
      columnDef.setHeaderCount(1);
      columnDef.setHeaderTruncatable(false);
      m_tableDefinition.addColumnDefinition(columnDef);
    }
    return m_tableDefinition;
  }

  public void createMetadataCol(MetadataColumnDef colDef) {
    MetadataColumn columnDef = new MetadataColumn(colDef.getName(), "");
    columnDef.setColumnSortable(true);
    columnDef.setColumnTruncatable(false);
    columnDef.setPreferredColumnWidth(50);
    columnDef.setMinimumColumnWidth(50);
    columnDef.setHeader(0, new HTML(colDef.getColumnName()));
    columnDef.setHeaderCount(1);
    columnDef.setHeaderTruncatable(false);
    m_tableDefinition.addColumnDefinition(columnDef);
  }

  private void createTableModel() {
    m_resultTableModel = new ResultTableModel();
    m_cachedTableModel = new CachedTableModel<ExecutingResult>(m_resultTableModel);
    m_cachedTableModel.setPreCachedRowCount(50);
    m_cachedTableModel.setPostCachedRowCount(50);
    m_cachedTableModel.setRowCount(1000);
  }

  private int findListElementByID(List<ExecutingResult> list, String id) {
    ExecutingResult r;
    for (int i = 0; i < list.size(); i++) {
      r = list.get(i);
      if (r.getId().equals(id))
        return i;
    }
    return -1;
  }

  public void refreshTable() {
    m_resultTableModel.setRowCount(m_data.size());
    m_cachedTableModel.clearCache();
    m_cachedTableModel.setRowCount(m_data.size());
    m_tableWidget.gotoPage(0, true);
  }

  @Override
  public void addResult(ExecutingResult result) {
    if (findListElementByID(m_data, result.getId()) == -1) {
      m_data.add(result);
      refreshTable();
    }
  }

  @Override
  public void updateResult(ExecutingResult result) {
    int index = findListElementByID(m_data, result.getId());
    if (index != -1) {
      m_data.set(index, result);
      refreshTable();
    }
  }

  @Override
  public Widget getWidget() {
    return m_flexTable;
  }

  @Override
  public void addResultTableEventListener(ResultTableEventListener listener) {
    m_eventListeners.add(listener);
  }

  protected void notifyListeners(ResultTableEvent e) {
    for (ResultTableEventListener l : m_eventListeners) {
      l.actionPerformed(e);
    }
  }

  class ResultTableModel extends com.google.gwt.gen2.table.client.MutableTableModel<ExecutingResult> {
    private Map<Integer, Comparator<ExecutingResult>> m_comparators = new HashMap<Integer, Comparator<ExecutingResult>>() {{
      put(0, ExecutingResultComparatorFactory.createComparator(ExecutingResultComparatorFactory.ComparatorType.BY_NAME));
      put(1, ExecutingResultComparatorFactory.createComparator(ExecutingResultComparatorFactory.ComparatorType.BY_NAME));
      put(2, ExecutingResultComparatorFactory.createComparator(ExecutingResultComparatorFactory.ComparatorType.BY_NAME));
      put(3, ExecutingResultComparatorFactory.createComparator(ExecutingResultComparatorFactory.ComparatorType.BY_TIME));
      put(4, ExecutingResultComparatorFactory.createComparator(ExecutingResultComparatorFactory.ComparatorType.BY_STATE));
    }};

    @Override
    public void requestRows(TableModelHelper.Request request, Callback<ExecutingResult> ExecutingResultCallback) {
      int sortColumn = request.getColumnSortList().getPrimaryColumn();
      boolean descending = !request.getColumnSortList().isPrimaryAscending();
      if (m_comparators.containsKey(sortColumn)) {
        Collections.sort(m_data, m_comparators.get(sortColumn));
        if (descending)
          Collections.reverse(m_data);
      }
      else {
        Collections.sort(m_data, m_comparators.get(3)); //by time
        Collections.reverse(m_data);
      }
      final List<ExecutingResult> resultList = m_data;
      ExecutingResultCallback.onRowsReady(request, new TableModelHelper.Response<ExecutingResult>() {
        @Override
        public Iterator<ExecutingResult> getRowValues() {
          return resultList.iterator();
        }
      });
    }

    @Override
    protected boolean onRowInserted(int i) {
      return true;
    }

    @Override
    protected boolean onRowRemoved(int i) {
      return true;
    }

    @Override
    protected boolean onSetRowValue(int i, ExecutingResult ExecutingResult) {
      return true;
    }
  }

  class PageNameColumn extends AbstractColumnDefinition<ExecutingResult, Anchor> {

    @Override
    public Anchor getCellValue(ExecutingResult rowValue) {
      return rowValue.getLink();
    }

    @Override
    public void setCellValue(ExecutingResult rowValue, Anchor cellValue) {
      //rowValue.setName(cellValue.getText());
    }
  }
  
  class MetadataColumn extends AbstractColumnDefinition<ExecutingResult, String> {
    
    private String m_metadataField;
    private String m_defaultValue;
    
    public MetadataColumn(String metadataField, String defaultValue) {
      m_metadataField = metadataField;
      m_defaultValue = defaultValue;
    }

    @Override
    public String getCellValue(ExecutingResult rowValue) {
      return rowValue.getMetadata().containsKey(m_metadataField) ?
             rowValue.getMetadata().get(m_metadataField) :
             m_defaultValue;
    }

    @Override
    public void setCellValue(ExecutingResult rowValue, String cellValue) {
      rowValue.getMetadata().put(m_metadataField, cellValue);
    }
  }

  private static DateTimeFormat df = DateTimeFormat.getFormat("dd.MM.yy HH:mm");

  class TimeColumn extends AbstractColumnDefinition<ExecutingResult, String> {

    @Override
    public String getCellValue(ExecutingResult rowValue) {
      return df.format(rowValue.getStartTime());
    }

    @Override
    public void setCellValue(ExecutingResult rowValue, String cellValue) {
      rowValue.setStartTime(df.parse(cellValue));

    }
  }

  class ProgressColumn extends AbstractColumnDefinition<ExecutingResult, String> {

    @Override
    public String getCellValue(ExecutingResult rowValue) {
      return rowValue.getState().toString();
    }

    @Override
    public void setCellValue(ExecutingResult rowValue, String cellValue) {
      rowValue.setState(ExecutingResult.ExecutingState.valueOf(cellValue));
    }
  }

  class SummaryColumn extends AbstractColumnDefinition<ExecutingResult, String> {

    @Override
    public String getCellValue(ExecutingResult rowValue) {
      return rowValue.getStatusString();
    }

    @Override
    public void setCellValue(ExecutingResult rowValue, String cellValue) {
      //non editable
    }
  }
}
